package com.example.apidocserver.domain.document.service.impl;

import com.example.apidocserver.domain.document.dao.ApiParamFieldDao;
import com.example.apidocserver.domain.document.entity.ApiParam;
import com.example.apidocserver.domain.document.entity.ApiParamField;
import com.example.apidocserver.domain.document.service.ApiParamFieldService;
import com.example.apidocserver.infrastructure.config.input.ApiYmlConfig;
import com.example.apidocserver.infrastructure.constants.SystemConstants;
import com.example.apidocserver.infrastructure.util.entity.ApiFieldUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 接口文档字段表(ApiParamField)表服务实现类
 *
 * @author makejava
 * @since 2023-02-24 22:56:57
 */
@Service("apiParamFieldService")
@Slf4j
public class ApiParamFieldServiceImpl implements ApiParamFieldService {
    @Resource
    private ApiParamFieldDao apiParamFieldDao;

    @Resource
    private ApiYmlConfig apiYmlConfig;

    /**
     * 通过ID查询单条数据
     *
     * @param id 主键
     * @return 实例对象
     */
    @Override
    public ApiParamField queryById(Integer id) {
        return this.apiParamFieldDao.queryById(id);
    }

    /**
     * 查询所有对象
     *
     * @return 查询结果
     */
    @Override
    public List<ApiParamField> queryAll() {
        return this.apiParamFieldDao.queryAll();
    }

    /**
     * 根据对象查询
     *
     * @param apiParamField 筛选条件
     * @return 查询结果
     */
    @Override
    public List<ApiParamField> queryByEntity(ApiParamField apiParamField) {
        return this.apiParamFieldDao.queryByEntity(apiParamField);
    }

    /**
     * 新增数据
     *
     * @param apiParamField 实例对象
     * @return 实例对象
     */
    @Override
    public ApiParamField insert(ApiParamField apiParamField) {
        this.apiParamFieldDao.insert(apiParamField);
        return apiParamField;
    }

    /**
     * 修改数据
     *
     * @param apiParamField 实例对象
     * @return 实例对象
     */
    @Override
    public ApiParamField update(ApiParamField apiParamField) {
        this.apiParamFieldDao.update(apiParamField);
        return this.queryById(apiParamField.getId());
    }

    /**
     * 通过主键删除数据
     *
     * @param id 主键
     * @return 是否成功
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteById(Integer id) {
        this.apiParamFieldDao.deleteRltByFieldId(id);
        return this.apiParamFieldDao.deleteById(id) > 0;
    }

    @Override
    public Map<Integer, List<ApiParamField>> queryByParamIds(Collection<Integer> paramIds, boolean onlyEnableFlag) {
        if (CollectionUtils.isEmpty(paramIds)) {
            return Collections.emptyMap();
        }

        List<ApiParamField> fieldList = apiParamFieldDao.queryByParamIds(paramIds, onlyEnableFlag);
        if (fieldList.isEmpty()) {
            return Collections.emptyMap();
        }

        return fieldList.stream().collect(Collectors.groupingBy(ApiParamField::getParamId));
    }

    @Override
    public void insertNewAuto(List<ApiParamField> noRltFieldList, Integer apiId) {
        if (CollectionUtils.isEmpty(noRltFieldList)) {
            return;
        }
        // 1.筛选出已有存在字段
        List<ApiParamField> newFieldList = noRltFieldList.stream()
                .filter(apiParamField -> Objects.isNull(apiParamField.getId())).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(newFieldList)) {
            return;
        }

        // 2.新增字段信息 或查询已有信息
        insertOrFindField(apiId, newFieldList);
    }

    @Override
    public void createRlt(List<ApiParamField> noRltFieldList, boolean orderResetFlag) {
        if (!CollectionUtils.isEmpty(noRltFieldList)) {
            // 1.根据参数对字段就行分类  去重
            Map<Integer, List<ApiParamField>> fieldByParamMap = noRltFieldList.stream()
                    .collect(Collectors.groupingBy(ApiParamField::getParamId,
                            Collectors.collectingAndThen(
                                    Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(ApiParamField::getId))),
                                    ArrayList::new)));

            // 2.设置字段排序
            List<ApiParamField> newRltList = new ArrayList<>();
            fieldByParamMap.forEach((key, fieldList) -> {
                newRltList.addAll(fieldList);
                if (orderResetFlag) {
                    Integer maxOrder = apiParamFieldDao.findMaxOrder(fieldList.get(0).getParamId());
                    int maxOrderNo = Objects.isNull(maxOrder) ? 0 : maxOrder;
                    for (ApiParamField field : fieldList) {
                        field.setOrderNo(++maxOrderNo);
                    }
                }
            });

            // 3.增加参数与字段关系
            this.apiParamFieldDao.upsertRlts(newRltList);
        }
    }

    @Override
    public List<ApiParamField> findLikeFields(String likeStr, Integer spaceId) {
        return apiParamFieldDao.queryLikeName(likeStr, spaceId);
    }

    @Override
    public void upsertField(ApiParam paramDescVo) {
        // 查询参数字段数据
        if (CollectionUtils.isEmpty(paramDescVo.getFields())) {
            return;
        }

        List<ApiParamField> fieldDbList = apiParamFieldDao.queryByParamIds(Collections.singleton(paramDescVo.getId()), false);
        List<ApiParamField> fieldDbLikeList = apiParamFieldDao.queryLike(paramDescVo.getFields());
        Map<String, Integer> keyAndIdMap = fieldDbLikeList.stream().collect(Collectors.toMap(ApiFieldUtil::getFieldUniKey, ApiParamField::getId));

        Date now = new Date();
        // 增加新增字段
        if (CollectionUtils.isEmpty(fieldDbList)) {
            // 新增参数字段
            paramDescAddFields(paramDescVo, keyAndIdMap, now);
        } else {
            // 修改参数字段
            paramDescUpsertField(paramDescVo, fieldDbList, keyAndIdMap, now);
        }
    }

    @Override
    public ApiParamField findByUinKey(ApiParamField field) {
        return apiParamFieldDao.queryUniKey(field);
    }

    @Override
    public List<ApiParamField> findBySpaceId(Integer spaceId) {
        return apiParamFieldDao.findBySpaceId(spaceId).stream()
                .sorted(Comparator.comparing(ApiParamField::getUpdateTime).reversed())
                .collect(Collectors.toList());
    }

    @Override
    public void upsertOne(ApiParamField field) {
        if (Objects.isNull(field.getId())) {
            apiParamFieldDao.insertOrUpdateBatch(Collections.singletonList(field));
        } else {
            ApiParamField fieldDb = apiParamFieldDao.findOtherUniKey(field);
            if (Objects.isNull(fieldDb)) {
                apiParamFieldDao.update(field);
            } else {
                apiParamFieldDao.updateParamField(field.getId(), fieldDb.getId());
                apiParamFieldDao.deleteById(field.getId());
            }
        }
    }

    @Override
    public void insertParamFieldBatch(List<ApiParam> apiParamList, Integer userId, Date now) {
        if (CollectionUtils.isEmpty(apiParamList)) {
            return;
        }
        // 1.获取所有字段对象、设置默认值
        List<ApiParamField> fieldList = new LinkedList<>();
        for (ApiParam apiParam : apiParamList) {
            if (CollectionUtils.isEmpty(apiParam.getFields())) {
                continue;
            }
            for (int i = 0; i < apiParam.getFields().size(); i++) {
                ApiParamField field = apiParam.getFields().get(i);
                field.setParamId(apiParam.getId()).setOrderNo(i + 1).setParamFieldDesc("")
                        .setDescMode(SystemConstants.DM_AFTER).setCreateTime(now).setUpdateTime(now)
                        .setCreator(userId).setUpdater(userId);
                fieldList.add(field);
            }
        }

        // 2.查询相似字段，查询数据库已字段、类型相同的的字段说明字段
        insertOrFindField(apiYmlConfig.commonApiId, new ArrayList<>(fieldList));

        // 3.绑定关系
        createRlt(fieldList, false);
    }

    private void insertOrFindField(Integer apiId, List<ApiParamField> newFieldList) {
        // 1.查询相似字段，查询数据库已字段、类型相同的的字段说明字段
        List<ApiParamField> fieldDbList = apiParamFieldDao.queryLike(newFieldList);

        if (fieldDbList.isEmpty()) {
            // 2.直接增加字段
            insertFieldList(newFieldList);
            return;
        }

        // 3.已有字段分组，过滤已有字段
        Map<Integer, Map<String, ApiParamField>> apiIdFieldKeyFieldMap = getApiIdFieldKeyFieldMap(fieldDbList, apiId);
        filterNewFieldAuto(newFieldList, apiIdFieldKeyFieldMap, apiId);

        // 4.新增字段
        if (!newFieldList.isEmpty()) {
            insertFieldList(newFieldList);
        }
    }

    /**
     * 参数说明字段更新
     *
     * @param paramDescVo 参数说明
     * @param fieldDbList 数据库已有字段
     * @param keyAndIdMap 唯一索引对应字段id
     * @param now         当前时间
     */
    private void paramDescUpsertField(ApiParam paramDescVo, List<ApiParamField> fieldDbList,
                                      Map<String, Integer> keyAndIdMap, Date now) {
        paramDescAddFields(paramDescVo, keyAndIdMap, now);
        Map<String, ApiParamField> keyAndFieldMap = fieldDbList.stream().collect(Collectors.toMap(ApiFieldUtil::getFieldUniKey, field -> field));

        List<ApiParamField> updateRltList = new ArrayList<>(paramDescVo.getFields().size());
        List<ApiParamField> insertRltList = new ArrayList<>(paramDescVo.getFields().size());
        for (ApiParamField apiParamField : paramDescVo.getFields()) {
            apiParamField.setParamId(paramDescVo.getId()).setUpdateTime(now);
            String fieldKey = ApiFieldUtil.getFieldUniKey(apiParamField);
            if (keyAndFieldMap.containsKey(fieldKey)) {
                updateRltList.add(apiParamField);
                fieldDbList.remove(keyAndFieldMap.get(fieldKey));
            } else {
                apiParamField.setCreateTime(now);
                insertRltList.add(apiParamField);
            }
        }
        createRlt(insertRltList, false);

        if (!updateRltList.isEmpty()) {
            apiParamFieldDao.upsertRlts(updateRltList);
        }
        if (!fieldDbList.isEmpty()) {
            apiParamFieldDao.deleteRltsUniKey(fieldDbList);
        }
    }

    /**
     * 参数说明增加字段说明
     *
     * @param paramDescVo 参数说明
     * @param keyAndIdMap 数据库已有相似字段
     * @param now         当前时间
     */
    private void paramDescAddFields(ApiParam paramDescVo, Map<String, Integer> keyAndIdMap, Date now) {
        fieldUpsertDo(paramDescVo, keyAndIdMap, now);

        // 3.接口参数绑定字段
        createRlt(paramDescVo.getFields(), false);
    }

    private void fieldUpsertDo(ApiParam paramDescVo, Map<String, Integer> keyAndIdMap, Date now) {
        // 1.设置字段
        if (keyAndIdMap.isEmpty()) {
            for (ApiParamField field : paramDescVo.getFields()) {
                field.setId(null).setParamId(paramDescVo.getId()).setCreateTime(now).setUpdateTime(now);
            }
        } else {
            for (ApiParamField field : paramDescVo.getFields()) {
                String fieldKey = ApiFieldUtil.getFieldKey(field);
                if (keyAndIdMap.containsKey(fieldKey)) {
                    field.setParamId(paramDescVo.getId()).setId(keyAndIdMap.get(fieldKey)).setUpdateTime(now);
                } else {
                    field.setParamId(paramDescVo.getId()).setId(null).setCreateTime(now).setUpdateTime(now);
                }
            }
        }

        // 2.字段新增或修改
        apiParamFieldDao.insertOrUpdateBatch(paramDescVo.getFields());

        List<ApiParamField> insertList = new ArrayList<>(paramDescVo.getFields());
        for (ApiParamField field : paramDescVo.getFields()) {
            if (Objects.isNull(field.getId())) {
                insertList.add(field);
            }
        }

        // 3.查询新增字段id
        if (!insertList.isEmpty()) {
            List<ApiParamField> dbList = apiParamFieldDao.queryUniKeys(insertList);
            for (ApiParamField dataDb : dbList) {
                String fieldDbKey = ApiFieldUtil.getFieldUniKey(dataDb);
                for (ApiParamField dataIn : insertList) {
                    if (fieldDbKey.equals(ApiFieldUtil.getFieldUniKey(dataIn))) {
                        insertList.remove(dataIn);
                        dataIn.setId(dataDb.getId());
                        break;
                    }
                }
            }
        }
    }

    /**
     * 新增字段执行
     *
     * @param fieldInList 新增字段集合
     */
    private void insertFieldList(List<ApiParamField> fieldInList) {
        List<ApiParamField> insertFieldList = new ArrayList<>(fieldInList.size());
        Map<String, List<ApiParamField>> keyAndFieldMap = new HashMap<>(fieldInList.size());
        for (ApiParamField field : fieldInList) {
            String fieldKey = ApiFieldUtil.getFieldKey(field);
            if (keyAndFieldMap.containsKey(fieldKey)) {
                // 重复字段
                keyAndFieldMap.get(fieldKey).add(field);
            } else {
                insertFieldList.add(field);
                keyAndFieldMap.put(fieldKey, new LinkedList<>());
            }
        }
        apiParamFieldDao.insertBatch(insertFieldList);

        if (keyAndFieldMap.size() != fieldInList.size()) {
            // 有重复字段
            for (ApiParamField fieldDb : insertFieldList) {
                String fieldKey = ApiFieldUtil.getFieldKey(fieldDb);
                if (!keyAndFieldMap.get(fieldKey).isEmpty()) {
                    for (ApiParamField field : keyAndFieldMap.get(fieldKey)) {
                        // 设置重复字段值
                        ApiFieldUtil.copyValueNoKey(fieldDb, field);
                        field.setId(fieldDb.getId());
                    }
                }
            }
        }
    }

    /**
     * 将已有字段按通用、非通用字段分组
     *
     * @param fieldList 已有字段集合
     * @param apiId     接口id
     * @return 通用非通用 key对应字段对象
     */
    private Map<Integer, Map<String, ApiParamField>> getApiIdFieldKeyFieldMap(List<ApiParamField> fieldList,
                                                                              Integer apiId) {
        // 返回数据
        Map<Integer, Map<String, ApiParamField>> apiIdAndFieldKeyAndFieldMap = new HashMap<>(3);
        int mapSize = fieldList.size() > 8 ? fieldList.size() >> 1 : 4;
        HashMap<String, ApiParamField> thisApiFieldMap = new HashMap<>(mapSize);
        HashMap<String, ApiParamField> commonFieldMap = new HashMap<>(mapSize);
        HashMap<String, ApiParamField> otherFieldMap = new HashMap<>(mapSize);
        boolean notCommon = !Objects.equals(apiYmlConfig.commonApiId, apiId);
        if (notCommon) {
            apiIdAndFieldKeyAndFieldMap.put(apiId, thisApiFieldMap);
        }
        apiIdAndFieldKeyAndFieldMap.put(apiYmlConfig.commonApiId, commonFieldMap);
        apiIdAndFieldKeyAndFieldMap.put(apiYmlConfig.otherApiId, otherFieldMap);

        // 按字段就行分类
        for (ApiParamField apiParamField : fieldList) {
            Integer fieldApiId = apiParamField.getApiId();
            String fieldKey = ApiFieldUtil.getFieldKey(apiParamField);
            if (notCommon && apiId.equals(fieldApiId)) {
                setDbFieldMap(thisApiFieldMap, apiParamField, fieldKey);
            } else if (apiYmlConfig.commonApiId.equals(fieldApiId)) {
                setDbFieldMap(commonFieldMap, apiParamField, fieldKey);
            } else {
                setDbFieldMap(otherFieldMap, apiParamField, fieldKey);
            }
        }
        return apiIdAndFieldKeyAndFieldMap;
    }

    /**
     * 过滤设置数据库已有字段
     * 可用于自动增加字段和增加手段修改字段，当自动时cyOldFieldFlag为true
     *
     * @param newFieldList          输入字段
     * @param apiIdFieldKeyFieldMap 数据已有字段
     * @param apiId                 接口id
     */
    private void filterNewFieldAuto(List<ApiParamField> newFieldList,
                                    Map<Integer, Map<String, ApiParamField>> apiIdFieldKeyFieldMap, Integer apiId) {
        Map<String, ApiParamField> keyAndFieldThisMap = apiIdFieldKeyFieldMap.get(apiId);
        Map<String, ApiParamField> keyAndFieldComMap = apiIdFieldKeyFieldMap.get(apiYmlConfig.commonApiId);
        Map<String, ApiParamField> keyAndFieldOthMap = apiIdFieldKeyFieldMap.get(apiYmlConfig.otherApiId);

        // gpt优化
        Iterator<ApiParamField> iterator = newFieldList.iterator();
        while (iterator.hasNext()) {
            ApiParamField fieldIn = iterator.next();
            String fieldKey = ApiFieldUtil.getFieldKey(fieldIn);
            // 优先设置本接口字段
            if (keyAndFieldThisMap.containsKey(fieldKey)) {
                iterator.remove();
                ApiFieldUtil.copyValueNoKey(keyAndFieldThisMap.get(fieldKey), fieldIn);
                continue;
            }

            // 优先设置通用字段
            if (keyAndFieldComMap.containsKey(fieldKey)) {
                iterator.remove();
                ApiFieldUtil.copyValueNoKey(keyAndFieldComMap.get(fieldKey), fieldIn);
                continue;
            }

            // 接口字段
            if (keyAndFieldOthMap.containsKey(fieldKey)) {
                ApiFieldUtil.copyValueNoKey(keyAndFieldOthMap.get(fieldKey), fieldIn);
                fieldIn.setApiId(apiId).setId(null);
            }
        }
    }

    /**
     * 增加map中的字段
     *
     * @param keyAndFieldMap 字段key对应字段说明对象
     * @param apiParamField  字段对象
     * @param fieldKey       字段key
     */
    private static void setDbFieldMap(HashMap<String, ApiParamField> keyAndFieldMap, ApiParamField apiParamField,
                                      String fieldKey) {
        if (keyAndFieldMap.containsKey(fieldKey)) {
            ApiParamField apiParamField1 = keyAndFieldMap.get(fieldKey);
            if (apiParamField.getUsePriority() > apiParamField1.getUsePriority()) {
                keyAndFieldMap.put(fieldKey, apiParamField);
            }
        } else {
            keyAndFieldMap.put(fieldKey, apiParamField);
        }
    }
}
